# -*- coding: utf-8 -*-
# @Author: Jerry
# @Date:   2022-03-10 13:40:05
# @Last Modified by:   Jerry
# @Last Modified time: 2022-03-10 14:46:27

# **********
# OpenCV中的图像处理 » 4_12_模板匹配
# http://www.woshicver.com

# **********
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
import os
rootpath = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
datapath = os.path.join(rootpath,'data')
imgpath = lambda name: os.path.join(datapath,name)


# **********
'''目标
在本章中，您将学习
- 使用模板匹配在图像中查找对象
- 你将看到以下功能：cv.matchTemplate()，cv.minMaxLoc()'''


# **********
'''理论
模板匹配是一种用于在较大图像中搜索和查找模板图像位置的方法。
为此，OpenCV带有一个函数 cv.matchTemplate()。
它只是将模板图像滑动到输入图像上（就像在2D卷积中一样），然后在模板图像下比较模板和输入图像的拼图。
OpenCV中实现了几种比较方法。（您可以检查文档以了解更多详细信息）。
它返回一个灰度图像，其中每个像素表示该像素的邻域与模板匹配的程度。

如果输入图像的大小为(WxH)，而模板图像的大小为(wxh)，则输出图像的大小将为(W-w + 1，H-h + 1)。
得到结果后，可以使用 cv.minMaxLoc()函数查找最大/最小值在哪。
将其作为矩形的左上角，并以(w，h)作为矩形的宽度和高度。

该矩形是您模板的区域。

注意 如果使用 cv.TM_SQDIFF 作为比较方法，则最小值提供最佳匹配。'''


# **********
'''OpenCV中的模板匹配
示例:
在梅西的照片中搜索他的脸(模板)，尝试所有比较方法，以便我们可以看到它们的结果如何'''

img = cv.imread(imgpath('messi5.jpg'),0) # 读取图形
img2 = img.copy() # copy
template = cv.imread(imgpath('messi5_template.jpg'),0) # 读取模板
w, h = template.shape[::-1]
# 列表中所有的6种比较方法
methods = ['cv.TM_CCOEFF',  # 4 最白亮的点
           'cv.TM_CCOEFF_NORMED', # 5 # 最白亮的点
           'cv.TM_CCORR' ,  # 2 # 结果表明，cv.TM_CCORR没有正确画出模板位置
           'cv.TM_CCORR_NORMED', # 3 # 最白亮的点
           'cv.TM_SQDIFF',  # 0 最黑的点
           'cv.TM_SQDIFF_NORMED', # 1 最黑的点
           ]
for meth in methods:
    img = img2.copy()
    # eval(source[, globals[, locals]]) -> value
    # Evaluate the source in the context of globals and locals.
    method = eval(meth) # 4,5,2,3,0,1
    # 应用模板匹配
    res = cv.matchTemplate(img,template,method)
    # ----------
    # plt.subplot(121),plt.imshow(res,cmap = 'gray')
    # plt.title('Matching Result'), plt.xticks([]), plt.yticks([])
    # ----------
    # res图形上的特征：
    # 'cv.TM_CCOEFF' = 4 ， 有一最白亮的点
    # 'cv.TM_CCOEFF_NORMED' = 5， 有一最白亮的点
    # 'cv.TM_CCORR' = 2，右下角有一白色区域
    # 'cv.TM_CCORR_NORMED' = 3， 有一最白亮的点
    # 'cv.TM_SQDIFF' = 0，有一最黑的点
    # 'cv.TM_SQDIFF_NORMED' = 1，有一最黑的点
    min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res) #从res中获取到坐标值
    # 如果方法是TM_SQDIFF或TM_SQDIFF_NORMED，则取最小值
    if method in [cv.TM_SQDIFF, cv.TM_SQDIFF_NORMED]:
        top_left = min_loc
    else:
        top_left = max_loc
    bottom_right = (top_left[0] + w, top_left[1] + h)
    # print top_left,bottom_right
    #top_left,bottom_right描述左上-右下的一个矩形，使用函数cv.rectangle()画出此矩形
    cv.rectangle(img,top_left, bottom_right, 255, 2)
    # ----------
    # plt.subplot(122),plt.imshow(img,cmap = 'gray')
    # plt.title('Detected Point'), plt.xticks([]), plt.yticks([])
    # plt.suptitle(meth)
    # plt.show()
    # ----------


# **********
'''多对象的模板匹配
在上一节中，我们在图像中搜索了梅西的脸，该脸在图像中仅出现一次。
假设您正在搜索具有多次出现的对象，则 cv.minMaxLoc()不会为您提供所有位置。在这种情况下，我们将使用阈值化。
因此，在此示例中，我们将使用著名游戏 Mario 的屏幕截图，并在其中找到硬币。'''
img_rgb = cv.imread(imgpath('mario4.jpeg'))
img_gray = cv.cvtColor(img_rgb, cv.COLOR_BGR2GRAY)
template = cv.imread(imgpath('mario4_template.png'),0)
w, h = template.shape[::-1]
res = cv.matchTemplate(img_gray,template,cv.TM_CCOEFF_NORMED)
threshold = 0.9
loc = np.where( res >= threshold)
for pt in zip(*loc[::-1]):
    # print pt
    cv.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,0,255), 1)
# cv.imwrite('res.png',img_rgb)
plt.imshow(cv.cvtColor(img_rgb,cv.COLOR_BGR2RGB))
plt.xticks([]),plt.yticks([])
plt.show()


# **********
# 总结：
# 对于res = cv.matchTemplate(img_gray,template,cv.TM_CCOEFF_NORMED)
# 所获取到的res中，包含了所有的匹配结果的坐标值
#
# 单对象的模板匹配，是从res中直接用函数cv.minMaxLoc()获取其最大、最小的坐标参数
# 而多对象模板匹配，是从res中获取超过某 threshold的值，将这些值做为有效的坐标参量





